﻿/*  ----------------------------------------------------------------------------
 *  SysServer
 *  ----------------------------------------------------------------------------
 *  File:       SpinWaitLock.cs
 *  Creater:     SEEN\MistSeen
 *  ----------------------------------------------------------------------------
 *  $Date: 2010-09-06 10:36:01 +0000 (Mon, 06 Sep 2010) $
 *  $Rev: 15 $
 *  $Author: ScorDeity $
 *  $URL: http://sysserver.googlecode.com/svn/trunk/src/SysServer/SpinWaitLock.cs $
 *  $Id: SpinWaitLock.cs 15 2010-09-06 10:36:01Z ScorDeity $
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace SysServer
{
    using System.Runtime.InteropServices;
    using System.Threading;
    /// <summary>
    /// 
    /// </summary>
    public struct SpinWaitLock
    {
        private const int LOCK_FREE = 0;
        private const int LOCK_OWNED = 1;
        private static readonly bool IsSingleCpuMachine = (Environment.ProcessorCount == 1);

        private int lockState;

        public void Enter()
        {
            Thread.BeginCriticalRegion();
            while (true)
            {
                // if lock is free, set it to owned and return
                if (Interlocked.Exchange(ref this.lockState, LOCK_OWNED) == LOCK_FREE)
                    return;
                // keep spinning until the lock appears to be free, at which point
                // we'll reloop and try to acquire the lock again.
                //
                // NOTE: doing a volatile read here is the fastest way to keep
                // checking the latest state of the lock, rather than attepting
                // to call Exchange over and over again.
                while (Thread.VolatileRead(ref this.lockState) == LOCK_OWNED)
                {
                    StallThread();
                }
            }
        }

        /// <summary>
        /// Releases the lock.
        /// </summary>
        public void Exit()
        {
            // mark the lock as free
            Interlocked.Exchange(ref this.lockState, LOCK_FREE);
            Thread.EndCriticalRegion();
        }


        private static void StallThread()
        {
            if (IsSingleCpuMachine)
            {
                // can't spin on a single CPU system, we're forced to yield our thread.
                SwitchToThread();
            }
            else
            {
                // spin here so other threads can run.
                Thread.SpinWait(1);
            }
        }

        [DllImport("kernel32", ExactSpelling = true)]
        private static extern void SwitchToThread();

    }
}